#include <stdio.h>
#include <assert.h>
#include <ctype.h>
#include <errno.h>
#include <string.h>
#include <stdlib.h>
#include <stdarg.h>
#include <fcntl.h>

#include <glog/logging.h>

#if defined(__linux__) || defined(__linux)
#include <sched.h>
#endif




bool safe_strtoull(const char *str, uint64_t *out) {
    assert(out != NULL);
    errno = 0;
    *out = 0;
    char *endptr;
    unsigned long long ull = strtoull(str, &endptr, 10);
    if (errno == ERANGE)
        return false;
    if (isspace(*endptr) || (*endptr == '\0' && endptr != str)) {
        if ((long long) ull < 0) {
            /* only check for negative signs in the uncommon case when
             * the unsigned number is so big that it's negative as a
             * signed number. */
            if (strchr(str, '-') != NULL) {
                return false;
            }
        }
        *out = ull;
        return true;
    }
    return false;
}

bool safe_strtoll(const char *str, int64_t *out) {
    assert(out != NULL);
    errno = 0;
    *out = 0;
    char *endptr;
    long long ll = strtoll(str, &endptr, 10);
    if (errno == ERANGE)
        return false;
    if (isspace(*endptr) || (*endptr == '\0' && endptr != str)) {
        *out = ll;
        return true;
    }
    return false;
}

bool safe_strtoul(const char *str, uint32_t *out) {
    char *endptr = NULL;
    unsigned long l = 0;
    assert(out);
    assert(str);
    *out = 0;
    errno = 0;

    l = strtoul(str, &endptr, 10);
    if (errno == ERANGE) {
        return false;
    }

    if (isspace(*endptr) || (*endptr == '\0' && endptr != str)) {
        if ((long) l < 0) {
            /* only check for negative signs in the uncommon case when
             * the unsigned number is so big that it's negative as a
             * signed number. */
            if (strchr(str, '-') != NULL) {
                return false;
            }
        }
        *out = l;
        return true;
    }

    return false;
}

bool safe_strtol(const char *str, int32_t *out) {
    assert(out != NULL);
    errno = 0;
    *out = 0;
    char *endptr;
    long l = strtol(str, &endptr, 10);
    if (errno == ERANGE)
        return false;
    if (isspace(*endptr) || (*endptr == '\0' && endptr != str)) {
        *out = l;
        return true;
    }
    return false;
}

bool safe_strtof(const char *str, float *out) {
    assert(out != NULL);
    errno = 0;
    *out = 0;
    char *endptr;
    float l = strtof(str, &endptr);
    if (errno == ERANGE)
        return false;
    if (isspace(*endptr) || (*endptr == '\0' && endptr != str)) {
        *out = l;
        return true;
    }
    return false;
}


#ifndef HAVE_HTONLL
static uint64_t mc_swap64(uint64_t in) {
#ifdef ENDIAN_LITTLE
    /* Little endian, flip the bytes around until someone makes a faster/better
    * way to do this. */
    int64_t rv = 0;
    int i = 0;
     for(i = 0; i<8; i++) {
        rv = (rv << 8) | (in & 0xff);
        in >>= 8;
     }
    return rv;
#else
    /* big-endian machines don't need byte swapping */
    return in;
#endif
}

uint64_t ntohll(uint64_t val) {
   return mc_swap64(val);
}

uint64_t htonll(uint64_t val) {
   return mc_swap64(val);
}
#endif



int lock_reg (int fd, int cmd, int type, off_t offset, int whence, off_t len)
{
    struct flock lock;
    lock.l_type = type;
    lock.l_start = offset;
    lock.l_whence = whence;
    lock.l_len = len;
    return (fcntl(fd, cmd, &lock));
}

#define write_lock(fd, offset, whence, len) \
    lock_reg(fd, F_SETLK, F_WRLCK, offset, whence, len)

int checkonly(const char *pid_file)
{
    pid_t pid;
    char buf[11];
    int val;
    int fd; 

    pid = getpid (); 
    sprintf(buf, "%d", (int)pid);

    fd = open (pid_file, O_WRONLY|O_CREAT, 0644);
    if (fd == -1) {   
        fprintf(stderr, "open '%s' failed!\n", strerror(errno));
        return -1;
    }

    if (write_lock (fd, 0, SEEK_SET, 0) < 0) {
        close (fd);
        return -2;
    }

    if (ftruncate (fd, 0) == -1) {
        close (fd);
        return -3;
    }

    write (fd, buf, strlen(buf));
    if ((val = fcntl (fd, F_GETFD, 0)) < 0) {
        close (fd);
        return -4;
    }
                                                                                                                                                                                 
    val |= FD_CLOEXEC;
    if (fcntl (fd, F_SETFD, val) < 0) {
        close (fd);
        return -5;
    }
    return fd;
}


void vperror(const char *fmt, ...) 
{
    int old_errno = errno;
    char buf[80];
    va_list ap;
    va_start(ap, fmt);
    vsnprintf(buf, sizeof(buf), fmt, ap);
    va_end(ap);
    errno = old_errno;
    perror(buf);
}

/**
 * used to bind thread to CPU if the system supports
 *
 * @param cpu, cpu index
 *
 * @return if success, return EXIT_SUCCESS, else return -1
 */
uint32_t set_thread_cpu_affinity(uint32_t cpu)
{
  uint32_t ret= 0;

#if defined(__linux__) || defined(__linux)
  cpu_set_t cpu_set;
  CPU_ZERO(&cpu_set);
  CPU_SET(cpu, &cpu_set);

  LOG(INFO) << "sched_setaffinity " << cpu;
  if (sched_setaffinity(0, sizeof(cpu_set_t), &cpu_set) == -1) {
    fprintf(stderr, "WARNING: Could not set CPU Affinity, continuing...\n");
    ret= 1;
  }
#endif

  return ret;
} /* set_thread_cpu_affinity */

